%% copy riak-0.14.2 source to learn dynamo implementation and erlang
%% author: lichuang
-module(mochiglobal).
-export([get/1, get/2, put/2]).

-spec get(atom()) -> any() | undefined.
get(Key) ->
  get(Key, undefined).

-spec get(atom(), T) -> any() | T.
get(Key, Default) ->
  get(Key, Default, key_to_module(Key)).

-spec key_to_module(atom()) -> atom().
key_to_module(Key) ->
  list_to_atom("mochiglobal:" ++ atom_to_list(Key)).

get(_Key, Default, Mod) ->
  try Mod:term()
  catch error:undef ->
    Default
  end.

-spec put(atom(), any()) -> ok.
put(Key, Value) ->
  put(Key, Value, key_to_module(Key)).

put(_Key, Value, Mod) ->
  Bin = compile(Mod, Value),
  code:purge(Mod),
  code:load_binary(Mod, atom_to_list(Mod) ++ ".erl", Bin),
  ok.

-spec compile(atom(), any()) -> binary().
compile(Module, Value) ->
  {ok, Module, Bin} = compile:forms(forms(Module, Value),
				    [verbose, report_errors]),
  Bin.

-spec forms(atom(), any()) -> [erl_syntax:syntaxTree()].
forms(Module, Value) ->
  [erl_syntax:revert(X) || X <- term_to_abstract(Module, term, Value)].

-spec term_to_abstract(atom(), atom(), any()) -> [erl_syntax:syntaxTree()].
term_to_abstract(Module, Getter, T) ->
    [%% -module(Module).
     erl_syntax:attribute(
       erl_syntax:atom(module),
       [erl_syntax:atom(Module)]),
     %% -export([Getter/0]).
     erl_syntax:attribute(
       erl_syntax:atom(export),
       [erl_syntax:list(
         [erl_syntax:arity_qualifier(
            erl_syntax:atom(Getter),
            erl_syntax:integer(0))])]),
     %% Getter() -> T.
     erl_syntax:function(
       erl_syntax:atom(Getter),
       [erl_syntax:clause([], none, [erl_syntax:abstract(T)])])].
